package com.stm.bi.report.salesTrend;

import com.stm.bi.core.util.DateUtils;
import com.stm.bi.core.util.ETLUtils;
import com.stm.bi.core.util.SqlUtils;
import com.stm.bi.dto.query.DimensionDataGroupQry;
import com.stm.bi.dto.query.GroupField;
import com.stm.bi.dto.query.SalesTrendLineChartQuery;
import com.stm.bi.dto.query.ods.OdsListQry;
import com.stm.bi.dto.vo.ValueVO;
import com.stm.bi.model.Chart;
import com.stm.bi.report.AbsChartQueryExe;
import com.stm.bi.report.util.DimensionPolicyUtils;
import com.stm.bi.repository.dataobject.ods.OdsWarehouseDO;
import com.stm.bi.repository.ods.OdsWarehouseRepository;
import com.stm.framework.dto.Response;
import com.stm.saas.api.PolicyDetailRemoteService;
import com.stm.saas.api.ProductConfigRemoteService;
import com.stm.saas.dto.vo.IndexVO;
import com.stm.saas.dto.vo.PolicyDetailVO;
import com.stm.saas.dto.vo.ProductConfigVO;
import com.stm.ucenter.api.DeptRemoteService;
import com.stm.ucenter.dto.query.org.DeptListQry;
import com.stm.ucenter.dto.vo.org.DeptVO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Stream;

import static com.stm.bi.constant.GroupTypeConstants.SUM;
import static com.stm.bi.constant.IndexConstants.*;
import static com.stm.bi.constant.SalesTrendConstants.*;

@Component
public class SalesTrendLineChartQueryExe extends AbsChartQueryExe<SalesTrendLineChartQuery, Chart<List<ValueVO<BigDecimal>>>> {

    @Resource
    private ProductConfigRemoteService productConfigRemoteService;
    @Resource
    private DeptRemoteService deptRemoteService;
    @Resource
    private OdsWarehouseRepository odsWarehouseRepository;
    @Resource
    private PolicyDetailRemoteService policyDetailRemoteService;

    @Override
    public Chart<List<ValueVO<BigDecimal>>> query(Long orgId, String orgNo, SalesTrendLineChartQuery query) {
        if (query.getDimension().matches("^\\d+$")) {
            List<PolicyDetailVO> policyDetailVOS = policyDetailRemoteService.selectByIds(List.of(Long.valueOf(query.getDimension()))).getData();
            query.setDimension(DimensionPolicyUtils.getPolicyByDimension(query.getDimension(), policyDetailVOS));
        }

        List<String> dates = DateUtils.getReportDateList(query.getStartDate(), query.getEndDate(), query.getDateType().getValue());
        String key = query.getDimension().split("_")[0];
        if(!key.equals(CUSTOMER)){
            key = key + query.getType();
        }
        List<IndexVO> indices = INDEX_MAP.get(key);
        List<Map> maps = getOriginalData(orgId, orgNo, query, indices, dates);
        return getChart(query, indices, dates, maps);
    }

    private List<Map> getOriginalData(Long orgId, String orgNo, SalesTrendLineChartQuery query, List<IndexVO> indices, List<String> dates) {
        String salesType = getSalesType(orgId);

        if (query.getDimension().matches("^\\d+$")) {
            List<PolicyDetailVO> policyDetailVOS = policyDetailRemoteService.selectByIds(List.of(Long.valueOf(query.getDimension()))).getData();
            query.setDimension(DimensionPolicyUtils.getPolicyByDimension(query.getDimension(), policyDetailVOS));
        }


        String key = query.getDimension().split("_")[0];

        DimensionDataGroupQry qry = new DimensionDataGroupQry()
                .setOrgNo(orgNo)
                .setDateType(query.getDateType().getValue())
                .setDeptId(query.getDeptId())
                .setSalesType(salesType)
                .setStartDate(dates.getFirst())
                .setEndDate(dates.getLast())
                .setGroupBy(List.of(DATE_FIELD))
                .setGroupFields(GROUP_FIELD_MAP.get(key));

        Map<String, Object> otherConditions = new HashMap<>();
        if(query.getStyleType() != null  && !query.getStyleType().isEmpty()){
            otherConditions.put(STYLE_TYPE_FIELD, query.getStyleType());
        }

        if (query.getStyleSeason() != null  && !query.getStyleSeason().isEmpty() && !Objects.equals(query.getStyleSeason(), "0")){
            otherConditions.put(PROD_SEASON_FIELD, query.getStyleSeason());
        }

        qry.setOtherConditions(otherConditions);

        String sql =  SqlUtils.buildSql(qry, TABLE_NAME.get(query.getDimension()));

        List<Map> maps = jdbcService.selectList(sql, new Object[]{});

        if(key.equals("format")){

//            qry.setGroupFields(List.of(GroupField.of(NUMBER_OF_TRANSACTIONS_FIELD, SUM),
//                    GroupField.of(AVG_DAY_NUMBER_OF_TRANSACTIONS_FIELD, SUM),
//                    GroupField.of(NUMBER_OF_CONT_ORDER_FIELD, AVG)));
//            sql =  SqlUtils.buildSql(qry, ATTD_TABLE_NAME.get(dimension));
//            List<Map> attdMaps = new ArrayList<>();
//            if(!Objects.equals(FORMAT_CHANNEL, query.getDimension())){
//                attdMaps = jdbcService.selectList(sql, new Object[]{});
//            }

            for(Map map : maps){
                BigDecimal sales = map.get(SALES_AMOUNT_FIELD) == null ? BigDecimal.ZERO : (BigDecimal) map.get(SALES_AMOUNT_FIELD);
                BigDecimal transactions = map.get(NUMBER_OF_TRANSACTIONS_FIELD) == null ? BigDecimal.ZERO : (BigDecimal) map.get(NUMBER_OF_TRANSACTIONS_FIELD);
                BigDecimal contOrder = map.get(NUMBER_OF_CONT_ORDER_FIELD) == null ? BigDecimal.ZERO : (BigDecimal) map.get(NUMBER_OF_CONT_ORDER_FIELD);
                BigDecimal dayTransactions = map.get(AVG_DAY_NUMBER_OF_TRANSACTIONS_FIELD) == null ? BigDecimal.ZERO : (BigDecimal) map.get(AVG_DAY_NUMBER_OF_TRANSACTIONS_FIELD);
                BigDecimal perTransaction = transactions.doubleValue() == 0 ? BigDecimal.ZERO : sales.divide(transactions, 2, RoundingMode.HALF_UP);

                map.put(NUMBER_OF_TRANSACTIONS_FIELD, transactions);
                map.put(AVG_DAY_NUMBER_OF_TRANSACTIONS_FIELD, dayTransactions);
                map.put(NUMBER_OF_CONT_ORDER_FIELD, contOrder.setScale(2, RoundingMode.HALF_UP));
                map.put(PER_CUSTOMER_TRANSACTION_FIELD, perTransaction);
            }

        }else if(key.equals("prod")) {

            Map<Long, Long> deptIdMap = getDeptIdMap(orgId, orgNo);
            qry.setDeptId(deptIdMap.get(query.getDeptId()))
                    .setGroupFields(List.of(GroupField.of(INVENTORY_FIELD, SUM)))
                    .setSalesType(null);
            sql =  SqlUtils.buildSql(qry, ATTD_TABLE_NAME.get(query.getDimension()));
            List<Map> attdMaps = jdbcService.selectList(sql, new Object[]{});
            for(Map map : maps){
                Map attdMap = attdMaps.stream()
                        .filter(m -> Objects.equals(m.get(DATE_FIELD), map.get(DATE_FIELD)))
                        .findAny().orElse(new HashMap());

                BigDecimal sales = map.get(SALES_AMOUNT_FIELD) == null ? BigDecimal.ZERO : (BigDecimal) map.get(SALES_AMOUNT_FIELD);
                BigDecimal qty = map.get(QTY_FIELD) == null ? BigDecimal.ZERO : (BigDecimal) map.get(QTY_FIELD);
                BigDecimal inventory = attdMap.get(INVENTORY_FIELD) == null ? BigDecimal.ZERO : (BigDecimal) attdMap.get(INVENTORY_FIELD);
                BigDecimal price = qty.doubleValue() == 0 ? BigDecimal.ZERO : sales.divide(qty, 2, RoundingMode.HALF_UP);
                BigDecimal rate = inventory.add(qty).doubleValue() == 0 ? BigDecimal.ZERO : qty.multiply(BigDecimal.valueOf(100)).divide(inventory.add(qty), 2, RoundingMode.HALF_UP);

                map.put(PRICE_AVG_FIELD, price);
                map.put(DIGESTIBILITY_FIELD, rate);
                map.put(INVENTORY_FIELD, inventory);
            }
        }else {
            for(Map map : maps){
                BigDecimal sales = map.get(SALES_AMOUNT_FIELD) == null ? BigDecimal.ZERO : (BigDecimal) map.get(SALES_AMOUNT_FIELD);
                BigDecimal transactions = map.get(NUMBER_OF_TRANSACTIONS_FIELD) == null ? BigDecimal.ZERO : (BigDecimal) map.get(NUMBER_OF_TRANSACTIONS_FIELD);
                BigDecimal perTransaction = transactions.doubleValue() == 0 ? BigDecimal.ZERO : sales.divide(transactions, 2, RoundingMode.HALF_UP);
                map.put(PER_CUSTOMER_TRANSACTION_FIELD, perTransaction);
            }
        }

        return maps;
    }

    private String getSalesType(Long orgId) {
        Response<ProductConfigVO> config = productConfigRemoteService.selectByOrgId(orgId);
        String salesType = "dealing";
        if(!config.isSuccess()){
            salesType = config.getData().getSalesDefinition();
        }

        return salesType;
    }

    private Chart<List<ValueVO<BigDecimal>>> getChart(SalesTrendLineChartQuery query, List<IndexVO> indices, List<String> dates, List<Map> maps) {
        Chart<List<ValueVO<BigDecimal>>> chart = new Chart<>();
        List<List<Object>> data = new ArrayList<>();
        List<String> items = chart.getItems();

        for(IndexVO index : indices){
            items.add(index.getName());
        }

        for(String date : dates){
            List<Object> row = new ArrayList<>();
            row.add(DateUtils.getReportDateName(date, query.getDateType().getValue()));
            Map map = maps.stream()
                    .filter(m -> Objects.equals(m.get("date"), date))
                    .findAny().orElse(new HashMap());

            for(IndexVO index : indices){
                BigDecimal d = map.get(index.getFieldName()) == null ? BigDecimal.ZERO : (BigDecimal) map.get(index.getFieldName());
                row.add(d);
            }
            data.add(row);
        }

        List<ValueVO<BigDecimal>> extend = getExtendData(indices, items, maps, data);

        chart.setDatas(data);
        chart.setItems(items);
        chart.setExtend(extend);
        return chart;
    }

    private List<ValueVO<BigDecimal>> getExtendData(List<IndexVO> indices, List<String> items, List<Map> maps, List<List<Object>> data) {

        List<ValueVO<BigDecimal>> extend = new ArrayList<>();

        for(IndexVO index : indices){

            Stream<BigDecimal> stream = maps.stream().map(m -> (BigDecimal) m.get(index.getFieldName()));
            // 计算累计值和累计平均值
            if(List.of(SALES_AMOUNT_FIELD, QTY_FIELD, NEW_CUSTOMERS_FILED, OLD_CUSTOMERS_FILED).contains(index.getFieldName())){
                BigDecimal total = stream.reduce(BigDecimal.ZERO, BigDecimal::add);
                extend.add(new ValueVO<BigDecimal>().setId(index.getId()).setCode("sum").setLabel(index.getName()).setValue(total));
            }else {
                BigDecimal avg;
                if(List.of(PER_CUSTOMER_TRANSACTION_FIELD, PRICE_AVG_FIELD).contains(index.getFieldName())){
                    BigDecimal sales = maps.stream()
                            .map(m -> (BigDecimal) m.get(SALES_AMOUNT_FIELD))
                            .reduce(BigDecimal.ZERO, BigDecimal::add);
                    BigDecimal qty = BigDecimal.ZERO;
                    if (PER_CUSTOMER_TRANSACTION_FIELD.equals(index.getFieldName())) {
                        qty = maps.stream()
                                .map(m -> (BigDecimal) m.get(NUMBER_OF_TRANSACTIONS_FIELD))
                                .reduce(BigDecimal.ZERO, BigDecimal::add);
                    }else if(PRICE_AVG_FIELD.equals(index.getFieldName())){
                        qty = maps.stream()
                                .map(m -> (BigDecimal) m.get(QTY_FIELD))
                                .reduce(BigDecimal.ZERO, BigDecimal::add);
                    }

                    avg = qty.doubleValue() == 0 ? BigDecimal.ZERO :sales.divide(qty, 2, RoundingMode.HALF_UP);

                }else if (DIGESTIBILITY_FIELD.equals(index.getFieldName())){
                    BigDecimal qty = maps.stream()
                            .map(m -> (BigDecimal) m.get(QTY_FIELD))
                            .reduce(BigDecimal.ZERO, BigDecimal::add);
                    BigDecimal inventory = maps.stream()
                            .map(m -> (BigDecimal) m.get(INVENTORY_FIELD))
                            .reduce(BigDecimal.ZERO, BigDecimal::add);

                    avg = inventory.add(qty).doubleValue() == 0 ? BigDecimal.ZERO : qty.multiply(BigDecimal.valueOf(100)).divide(inventory.add(qty), 2, RoundingMode.HALF_UP);
                }else {
                    OptionalDouble average = stream.mapToDouble(BigDecimal::doubleValue).average();

                    if(average.isPresent()){
                        avg = BigDecimal.valueOf(average.getAsDouble()).setScale(2, RoundingMode.HALF_UP);
                    } else {
                        avg = BigDecimal.ZERO;
                    }
                }

                if(!DIGESTIBILITY_FIELD.equals(index.getFieldName())){
                    items.add("累计平均：" + index.getName());
                    data.forEach(row -> row.add(avg));
                }

                extend.add(new ValueVO<BigDecimal>().setId(index.getId()).setCode("avg").setLabel(index.getName()).setValue(avg));
            }
        }

        return extend;
    }

    private Map<Long, Long> getDeptIdMap(Long orgId, String orgNo) {
        //仓库与部门的对应关系逻辑
        Response<List<DeptVO>> deptResp = deptRemoteService.selectList(new DeptListQry().setOrgId(orgId));
        if(!deptResp.isSuccess()){
            log.error("部门查询失败:{}", deptResp.getMessage());
            return new HashMap<>();
        }

        List<DeptVO> depts = deptResp.getData();
        Map<Long, Long> deptMap = new HashMap<>();
        depts.forEach(d -> deptMap.put(d.getId(), d.getParentId()));

        List<OdsWarehouseDO> warehouses = odsWarehouseRepository.selectList(new OdsListQry().setOrgNo(orgNo));
        List<Long> deptIds = warehouses.stream().map(OdsWarehouseDO::getDeptId).distinct().filter(Objects::nonNull).toList();

        //获取对应的仓库部门对应关系MAP
        return ETLUtils.getWarehouseDeptIdMap(deptMap, deptIds);
    }

    @Override
    public String getReportName() {
        return null;
    }

}
